package com.taobao.tbapi;

import java.util.concurrent.Callable;

import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.taobao.api.ApiException;
import com.taobao.api.AutoRetryTaobaoClient;
import com.taobao.api.DefaultTaobaoClient;
import com.taobao.api.TaobaoClient;
import com.taobao.api.TaobaoRequest;
import com.taobao.api.TaobaoResponse;

public abstract class TBApi<REQ extends TaobaoRequest<REP>, REP extends TaobaoResponse, RES> implements Callable<RES> {

  private static final Logger log          = LoggerFactory.getLogger(TBApi.class);
  protected String            TAG          = "TBApi";
  protected String            sid;
  protected Long              userId;
  protected int               iteratorTime = 1;

  public TBApi(Long userId, String sid) {
    this.userId = (userId == null) ? (-1L) : userId;
    this.sid = sid;
  }

  public TBApi(String sid) {
    this.sid = sid;

  }

  public abstract REQ prepareRequest();

  public abstract RES validResponse(REP resp);

  public abstract RES applyResult(RES res);

  TaobaoClient client = null;

  @Override
  public RES call() throws TBApiBizException, TBApiServerException {
    Thread.currentThread().setName(TAG);
    // not use auto retry client 2013-12-03
    return call(genClient());
  }

  public RES call(TaobaoClient _client) throws TBApiBizException, TBApiServerException {

    if (_client != null) {
      this.client = _client;
    }

    RES res = null;

    while (iteratorTime-- > 0) {
      REP resp = null;
      REQ req = null;

      req = prepareRequest();

      resp = execute(req);

      res = validResponse(resp);

      res = applyResult(res);
    }

    return res;
  }

  protected REP execute(REQ req) throws TBApiServerException, TBApiBizException {

    REP resp = null;
    try {
      if (sid == null) {
        resp = this.client.execute(req);
      } else {
        resp = this.client.execute(req, sid);
      }
    } catch (ApiException e) {
      throw TBApiServerException.genException(e);
    }

    if ((resp == null) || !resp.isSuccess()) {
      handlerErrorCode(resp);
      throw TBApiBizException.genException(userId, resp);
    }
    return resp;
  }

  public void handlerErrorCode(REP response) {
    if (response.isSuccess()) {
      return;
    }

    log.error("error code:" + response.getErrorCode() + ",error msg:" + response.getMsg() + ", subcode:" + response.getSubCode() + ", sub msg:"
        + response.getSubMsg());

    // session is expired
    if ("27".equals(response.getErrorCode()) && "Invalid session:session-expired".equals(response.getMsg())) {
      // TODO update session
      // UserDao.setUserUnVaild(sid);
    }
  }

  public static TaobaoClient genClient() {
    return new DefaultTaobaoClient(ApiConfigs.API_TAOBAO_URL, ApiConfigs.APP_KEY, ApiConfigs.APP_SECRET);
  }

  public static TaobaoClient genAutoRetryClient() {
    return new AutoRetryTaobaoClient(ApiConfigs.API_TAOBAO_URL, ApiConfigs.APP_KEY, ApiConfigs.APP_SECRET);
  }

  public static TaobaoClient genAutoRetryClient(String appkey, String secret) {
    return new AutoRetryTaobaoClient(ApiConfigs.API_TAOBAO_URL, appkey, secret);
  }

  public static boolean shouldRetry(Throwable t) {
    String errorMessage = getErrorMessage(t);
    return shouldRetry(errorMessage);
  }

  public static boolean shouldRetry(TaobaoResponse rep) {
    String errorMessage = getErrorMessage(rep);
    return shouldRetry(errorMessage);
  }

  public static boolean shouldRetry(String message) {
    if (StringUtils.isEmpty(message)) {
      return false;
    }
    if (message.contains("api-access-count") || message.contains("second") || message.contains("App Call Limited")) {
      return true;
    }
    return false;
  }

  public static String getErrorMessage(TaobaoResponse rep) {

    String message = null;
    if (rep == null) {
      return "未知异常";
    }
    message = rep.getMsg();
    if (StringUtils.isEmpty(message)) {
      message = rep.getSubMsg();
    } else if (StringUtils.isEmpty(message)) {
      message = rep.getErrorCode();
    } else if (StringUtils.isEmpty(message)) {
      message = rep.getSubCode();
    } else if (StringUtils.isEmpty(message)) {
      message = "未知异常";
    }
    return message;
  }

  public static String getErrorMessage(Throwable t) {
    if (t == null) {
      return "未知异常";
    }
    if (StringUtils.isNotEmpty(t.getMessage())) {
      return t.getMessage();
    }
    return t.getClass().toString();
  }
}
